Create by fall on 22 Sep 2021 Recently revised in 27 Feb 2023
迭代器
Iterator
迭代器,提供了遍历访问数据的机制,部署在可迭代的数据结构内部或者原型上。
一个对象要能够成为迭代器,它必须有一个 next()
方法,每次执行 next()
方法会返回一个对象,这个对象包含了一个 done
属性(是个布尔值,true
表示可以继续下次迭代)和一个 value
属性(每次迭代的值)
// 手写一个迭代器
function makeIterator (arr){
let index = 0
return {
next(){
return index < arr.length ? {
value:arr[index++],
done:false
}:{done:true}
}
}
}
let blah = makeIterator([10,20,45])
iterable
可迭代数据结构:内部或者原型上必须有一个 Symbol.iterator
属性(如果是异步的则是 Symbol.asyncIterator
),这个属性是一个函数,执行后会生成一个迭代器:
let obj = {
[Symbol.iterator]() {
return {
index: 0,
next() {
if (this.index < 3) {
return {value: this.index++, done: false}
} else {
return {done: true}
}
}
}
}
}
for (let x of obj) {
console.log(x) // 0 1 2
}
内置的一些可迭代数据结构有:String
、Array
、TypedArray
、Map
和 Set
、arguments
、NodeList
:
let si = 'hi'[Symbol.iterator]()
si // StringIterator
si.next() // {value: 'h', done: false}
si.next() // {value: 'i', done: false}
si.next() // {value: undefined, done: true}
for...of
:用于遍历可迭代数据结构:
- 遍历字符串:
for...in
获取索引,for...of
获取值; - 遍历数组:
for...in
获取索引,for...of
获取值; - 遍历对象:
for...in
获取键,for...of
需自行部署[Symbol.iterator]
接口; - 遍历
Set
:for...of
获取值,for (const v of set)
; - 遍历
Map
:for...of
获取键值对,for (const [k, v] of map)
; - 遍历类数组:包含
length
的对象、arguments
对象、NodeList
对象(无Iterator
接口的类数组可用Array.from()
转换);
// 迭代字符串
for (let x of 'abc') {
console.log(x) // 'a' 'b' 'c'
}
// 迭代数组
for (let x of ['a', 'b', 'c']) {
console.log(x) // 'a' 'b' 'c'
}
// 遍历 Set
let set = new Set(['a', 'b', 'c'])
for (let x of set) {
console.log(x) // 'a' 'b' 'c'
}
// 遍历 Map
let map = new Map([['name', '布兰'], ['age', 12]])
for (let [key, value] of map) {
console.log(key + ': ' + value) // 'name: 布兰' 'age: 12'
}
for...of
和 for...in
对比
共同点:能够通过 break
、continue
和 return
跳出循环
不同点: - for...in
的特点:只能遍历键,会遍历原型上属性,遍历无顺序,适合于对象的遍历; - for...of
的特点:能够遍历值(某些数据结构能遍历键和值,比如 Map
),不会遍历原型上的键值,遍历顺序为数据的添加顺序,适用于遍历可迭代数据结构
Generator
-
生成器函数内部执行一段代码,遇到 yield 关键字,javascript 引擎返回关键字后面的内容给外部,并且暂停该函数的执行;
-
外部函数可以同步 next 方法恢复函数的执行;
function*
会生成一个函数,该函数不会立即执行,而是返回一个 Generator
对象,这个 Generator
是可以被迭代的。
- 可以实现暂停执行和恢复执行
- 返回的
Generator
对象有两个key
,一个是 value 另一个是 done。 - 如果遇到关键字
yield
就会停止,并且返回该关键字后面的内容作为 value 进行返回 - 再次调用
.next()
会继续执行,直到遇到下一个yield
关键字,或者return
并且返回return
中的内容 - 同
function
中的return
如果不设置,会返回undefined
示例一
function* gen(){
console.log(1)
yield 'once'
console.log(2)
yield 'twice'
console.log(3)
return 'end'
console.log(4)
}
let go = gen()
go.next()
// 1 -- log 输出
// { value: "once", done: false }
go.next()
// 2 -- log 输出
// { value: "twice", done: false }
go.next()
// 3 -- log 输出
// { value: "end", done: ture }
go.next() // { value: undefined, done: ture }
// 如果没有 return
function* gen2(){
yield 'once'
}
let go2 = gen2()
go2.next() // { value: "once", done: false }
go2.next() // { value: undefined, done: ture }
yield
后面可以添加 *
可以实现多次迭代。
function* inner() {
yield* [1, 2] // 如果向 yield 后面添加 *,此处表示,先返回 1 作为 value,然后返回 2 作为 value
return 'fall'
}
let i = inner()
i.next() // 1
yield 关键字还可以调用函数
function* outer(){
const result = yield* inner() // result 会被赋值为 return 的值,并且上个函数的 return 不会被输出,只会被接受到
// 并且如果不在 yield 后面添加 * 会直接把 inner() 作为 value 进行返回
console.log(result)
}
let i = outer()
i.next() // { value: 1, done: false }
i.next() // { value: 1, done: false }
i.next() // fall {value:undefined,done:true}
实例方法
Generator.prototype.next()
该方法会返回一个带有value
done
的对象。如果传入了参数,那么这个参数会传给上一条执行的 yield
语句左边的变量
function* f(es) {
console.log(es)
let a = yield 12
console.log(a)
let b = yield a
console.log(b)
}
let g = f()
console.log(g.next('1212')) // 第一个next里面的值应该会被废弃
console.log(g.next('1313')) // 可以通过 next 中传值,赋值给 a
console.log(g.next('9999'))
// {value: 12, done: false}
// '1313'
// {value: 'b', done: false}
// '9999'
// {value: undefined, done: true}
Generator.prototype.throw()
用于向生成器中抛出异常
function* gen() {
try {
yield 'a'
} catch(e) {
console.log(e)
}
yield 'b'
}
let g = gen()
g.next()
g.throw('error a')
g.next()
// {value: "a", done: false}
// 'error a'
// {value: "b", done: false}
// {value: undefined, done: true}
如果没有补货错误,后面的代码将不会被执行
function* gen() {
yield 'a'
yield 'b'
yield 'c'
}
let g = gen()
g.next()
try {
g.throw('error a')
} catch(e) {
console.log(e)
}
g.next()
// {value: "a", done: false}
// 'error a'
// {value: undefined, done: true}
Generator.prototype.return()
直接结束执行
function* gen() {
yield 1
yield 2
yield 3
}
let g = gen()
g.next() // { value: 1, done: false }
g.return('foo') // {value :"foo",done:true}
应用
将异步操作同步化
比如同时有多个请求,多个请求之间是有顺序的,只能等前面的请求完成了才请求后面的:
function* main() {
let res1 = yield request('a')
console.log(res1)
let res2 = yield request('b')
console.log(res2)
let res3 = yield request('c')
console.log(res3)
}
function request(url) {
setTimeout(function(){ // 模拟异步请求
it.next(url)
}, 300)
}
let it = main()
it.next()
// 'a' 'b' 'c'
给对象部署 Iterator
接口:
function* iterEntries(obj) {
let keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
let key = keys[i]
yield [key, obj[key]]
}
}
obj = { foo: 3, bar: 7 }
for (let [key, value] of iterEntries(obj)) {
console.log(key, value)
}
// foo 3
// bar 7
参考文章
作者 | 链接 |
---|---|
大海我来了 | https://juejin.cn/post/6895898051559456776 |